package main

import (
	"reflect"
	"strconv"
	"fmt"
	//"os"
	"strings"
	"time"
)

// Any formats any value as a string.
func Any(value interface{}) string {
	return formatAtom(reflect.ValueOf(value))
}

// formatAtom formats a value without inspecting its internal structure.
func formatAtom(v reflect.Value) string {
	switch v.Kind() {
	case reflect.Invalid:
		return "invalid"
	case reflect.Int, reflect.Int8, reflect.Int16,
		reflect.Int32, reflect.Int64:
		return strconv.FormatInt(v.Int(), 10)
	case reflect.Uint, reflect.Uint8, reflect.Uint16,
		reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return strconv.FormatUint(v.Uint(), 10)
		// ...floating-point and complex cases omitted for brevity...
	case reflect.Bool:
		return strconv.FormatBool(v.Bool())
	case reflect.String:
		return strconv.Quote(v.String())
	case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Slice, reflect.Map:
		return v.Type().String() + " 0x" +
			strconv.FormatUint(uint64(v.Pointer()), 16)
	default: // reflect.Array, reflect.Struct, reflect.Interface
		return v.Type().String() + " value"
	}
}

func Print(x interface{}) {
	v := reflect.ValueOf(x)
	t := v.Type()
	fmt.Printf("type %s\n", t)

	for i := 0; i < v.NumMethod(); i++ {
		methType := v.Method(i).Type()
		fmt.Printf("func (%s) %s%s\n", t, t.Method(i).Name,
			strings.TrimPrefix(methType.String(), "func"))
	}
}

func main() {

	//var x int64 = 1
	//var d time.Duration = 1 * time.Nanosecond
	//fmt.Println(Any(x))                  // "1"
	//fmt.Println(Any(d))                  // "1"
	//fmt.Println(Any([]int64{x}))         // "[]int64 0x8202b87b0"
	//fmt.Println(Any([]time.Duration{d})) // "[]time.Duration 0x8202b87e0"

	//x := 2                   // value   type    variable?
	//a := reflect.ValueOf(2)  // 2       int     no
	//b := reflect.ValueOf(x)  // 2       int     no
	//c := reflect.ValueOf(&x) // &x      *int    no
	//d := c.Elem()            // 2       int     yes (x)
	//
	//fmt.Println(a.CanAddr()) // "false"
	//fmt.Println(b.CanAddr()) // "false"
	//fmt.Println(c.CanAddr()) // "false"
	//fmt.Println(d.CanAddr()) // "true"
	//
	////x := 2
	////d := reflect.ValueOf(&x).Elem()   // d refers to the variable x
	//px := d.Addr().Interface().(*int) // px := &x
	//*px = 3                           // x = 3
	//fmt.Println(x)                    // "3"
	//
	//d.Set(reflect.ValueOf(4))
	//fmt.Println(x) // "4"

	//x := 1
	//rx := reflect.ValueOf(&x).Elem()
	//rx.SetInt(2)                     // OK, x = 2
	//rx.Set(reflect.ValueOf(3))       // OK, x = 3
	//rx.SetString("hello")            // panic: string is not assignable to int
	//rx.Set(reflect.ValueOf("hello")) // panic: string is not assignable to int
	//
	//var y interface{}
	//ry := reflect.ValueOf(&y).Elem()
	//ry.SetInt(2)                     // panic: SetInt called on interface Value
	//ry.Set(reflect.ValueOf(3))       // OK, y = int(3)
	//ry.SetString("hello")            // panic: SetString called on interface Value
	//ry.Set(reflect.ValueOf("hello")) // OK, y = "hello"

	//fmt.Println("OK")
	//
	//stdout := reflect.ValueOf(os.Stdout).Elem() // *os.Stdout, an os.File var
	//fmt.Println(stdout.Type())                  // "os.File"
	//fd := stdout.FieldByName("fd")
	//fmt.Println(fd.Int()) // "1"
	////fd.SetInt(2)          // panic: unexported field
	//
	//fmt.Println(fd.CanAddr(), fd.CanSet()) // "true false"

	Print(time.Hour)
	Print(new(strings.Replacer))

}
